package org.wheel.jpa.factory;

import org.hibernate.*;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.spi.CacheImplementor;
import org.hibernate.cfg.Settings;
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.hibernate.dialect.function.SQLFunctionRegistry;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.profile.FetchProfile;
import org.hibernate.engine.query.spi.QueryPlanCache;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.engine.spi.SessionBuilderImplementor;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.internal.FastSessionServices;
import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.metamodel.spi.MetamodelImplementor;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.query.spi.NamedQueryRepository;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.type.Type;
import org.hibernate.type.TypeResolver;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.persistence.Query;
import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

/**
 * @author klaus
 * @date 2021/9/1 华小勇 14:16
 */
public class MultableSessionFactory implements SessionFactoryImplementor {

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    private SessionFactoryImplementor delegate;

    private Supplier<EntityManagerFactoryBuilder> builderSupplier;

    public MultableSessionFactory(Supplier<EntityManagerFactoryBuilder> builderSupplier) {
        this.builderSupplier = builderSupplier;
        this.build();
    }

    protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilder() {
        return this.builderSupplier.get();
    }

    /**
     * 重建sessionFactory
     */
    public void reBuild() {
        SessionFactoryImplementor old = this.delegate;
        synchronized (this.delegate) {
            this.build();
            if (!old.isClosed()) {
                old.close();
            }
        }
    }

    /**
     * 创建sessionFactory
     */
    void build() {
        this.delegate = (SessionFactoryImplementor) this.getEntityManagerFactoryBuilder().build();
    }

    protected SessionFactoryImplementor delegate() {
        return this.delegate;
    }

    @Override
    public SessionFactoryOptions getSessionFactoryOptions() {
        return this.delegate().getSessionFactoryOptions();
    }

    @Override
    public SessionBuilderImplementor withOptions() {
        return this.delegate().withOptions();
    }

    @Override
    public Session openSession() throws HibernateException {
        return this.delegate().openSession();
    }

    @Override
    public Session getCurrentSession() throws HibernateException {
        return this.delegate().getCurrentSession();
    }

    @Override
    public StatelessSessionBuilder withStatelessOptions() {
        return this.delegate().withStatelessOptions();
    }

    @Override
    public StatelessSession openStatelessSession() {
        return this.delegate().openStatelessSession();
    }

    @Override
    public StatelessSession openStatelessSession(Connection connection) {
        return this.delegate().openStatelessSession(connection);
    }

    @Override
    public ClassMetadata getClassMetadata(Class entityClass) {
        return this.delegate().getClassMetadata(entityClass);
    }

    @Override
    public ClassMetadata getClassMetadata(String entityName) {
        return this.delegate().getClassMetadata(entityName);
    }

    @Override
    public CollectionMetadata getCollectionMetadata(String roleName) {
        return this.delegate().getCollectionMetadata(roleName);
    }

    @Override
    public Map<String, ClassMetadata> getAllClassMetadata() {
        return this.delegate().getAllClassMetadata();
    }

    @Override
    public Map getAllCollectionMetadata() {
        return this.delegate().getAllCollectionMetadata();
    }

    @Override
    public StatisticsImplementor getStatistics() {
        return this.delegate().getStatistics();
    }

    @Override
    public void close() throws HibernateException {
        this.delegate().close();
    }

    @Override
    public boolean isClosed() {
        return this.delegate().isClosed();
    }

    @Override
    public CacheImplementor getCache() {
        return this.delegate().getCache();
    }

    @Override
    public PersistenceUnitUtil getPersistenceUnitUtil() {
        return this.delegate().getPersistenceUnitUtil();
    }

    @Override
    public void addNamedQuery(String name, Query query) {
        this.delegate().addNamedQuery(name, query);
    }

    @Override
    public <T> T unwrap(Class<T> cls) {
        if (cls == this.getClass()) {
            return cls.cast(this);
        }
        return this.delegate().unwrap(cls);
    }

    @Override
    public <T> void addNamedEntityGraph(String graphName, EntityGraph<T> entityGraph) {
        this.delegate().addNamedEntityGraph(graphName, entityGraph);
    }

    @Override
    public Set getDefinedFilterNames() {
        return this.delegate().getDefinedFilterNames();
    }

    @Override
    public FilterDefinition getFilterDefinition(String filterName) throws HibernateException {
        return this.delegate().getFilterDefinition(filterName);
    }

    @Override
    public boolean containsFetchProfileDefinition(String name) {
        return this.delegate().containsFetchProfileDefinition(name);
    }

    @Override
    public TypeHelper getTypeHelper() {
        return this.delegate().getTypeHelper();
    }

    /**
     * @deprecated
     */
    @Override
    @Deprecated
    public TypeResolver getTypeResolver() {
        return this.delegate().getTypeResolver();
    }

    @Override
    public Map<String, Object> getProperties() {
        return this.delegate().getProperties();
    }

    @Override
    public JdbcServices getJdbcServices() {
        return this.delegate().getJdbcServices();
    }

    @Override
    public Interceptor getInterceptor() {
        return this.delegate().getInterceptor();
    }

    @Override
    public QueryPlanCache getQueryPlanCache() {
        return this.delegate().getQueryPlanCache();
    }


    @Override
    public RootGraphImplementor findEntityGraphByName(String name) {
        return this.delegate().findEntityGraphByName(name);
    }


    @Override
    public IdentifierGenerator getIdentifierGenerator(String rootEntityName) {
        return this.delegate().getIdentifierGenerator(rootEntityName);
    }

    @Override
    public Settings getSettings() {
        return this.delegate().getSettings();
    }

    @Override
    public Session openTemporarySession() throws HibernateException {
        return this.delegate().openTemporarySession();
    }

    @Override
    public EntityNotFoundDelegate getEntityNotFoundDelegate() {
        return this.delegate().getEntityNotFoundDelegate();
    }

    @Override
    public SQLFunctionRegistry getSqlFunctionRegistry() {
        return this.delegate().getSqlFunctionRegistry();
    }

    @Override
    public FetchProfile getFetchProfile(String name) {
        return this.delegate().getFetchProfile(name);
    }

    @Override
    public ServiceRegistryImplementor getServiceRegistry() {
        return this.delegate().getServiceRegistry();
    }

    @Override
    public void addObserver(SessionFactoryObserver observer) {
        this.delegate().addObserver(observer);
    }

    @Override
    public CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy() {
        return this.delegate().getCustomEntityDirtinessStrategy();
    }

    @Override
    public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() {
        return this.delegate().getCurrentTenantIdentifierResolver();
    }

    @Override
    public NamedQueryRepository getNamedQueryRepository() {
        return this.delegate().getNamedQueryRepository();
    }

    @Override
    public FastSessionServices getFastSessionServices() {
        return this.delegate().getFastSessionServices();
    }

    @Override
    public DeserializationResolver getDeserializationResolver() {
        return this.delegate().getDeserializationResolver();
    }

    @Override
    public IdentifierGeneratorFactory getIdentifierGeneratorFactory() {
        return this.delegate().getIdentifierGeneratorFactory();
    }

    @Override
    public Type getIdentifierType(String className) throws MappingException {
        return this.delegate().getIdentifierType(className);
    }

    @Override
    public String getIdentifierPropertyName(String className) throws MappingException {
        return this.delegate().getIdentifierPropertyName(className);
    }

    @Override
    public Type getReferencedPropertyType(String className, String propertyName) throws MappingException {
        return this.delegate().getReferencedPropertyType(className, propertyName);
    }

    @Override
    public String getUuid() {
        return this.delegate().getUuid();
    }

    @Override
    public String getName() {
        return this.delegate().getName();
    }

    @Override
    public Reference getReference() throws NamingException {
        return this.delegate().getReference();
    }

    @Override
    public <T> List<RootGraphImplementor<? super T>> findEntityGraphsByJavaType(Class<T> entityClass) {
        return this.delegate().findEntityGraphsByJavaType(entityClass);
    }

    @Override
    public EntityManager createEntityManager() {
        return this.delegate().createEntityManager();
    }

    @Override
    public EntityManager createEntityManager(Map map) {
        return this.delegate().createEntityManager(map);
    }

    @Override
    public EntityManager createEntityManager(SynchronizationType synchronizationType) {
        return this.delegate().createEntityManager(synchronizationType);
    }

    @Override
    public EntityManager createEntityManager(SynchronizationType synchronizationType, Map map) {
        return this.delegate().createEntityManager(synchronizationType, map);
    }

    @Override
    public CriteriaBuilder getCriteriaBuilder() {
        return this.delegate().getCriteriaBuilder();
    }

    @Override
    public MetamodelImplementor getMetamodel() {
        return this.delegate().getMetamodel();
    }

    @Override
    public boolean isOpen() {
        return this.delegate().isOpen();
    }

    @Override
    public Type resolveParameterBindType(Object bindValue) {
        return this.delegate().resolveParameterBindType(bindValue);
    }

    @Override
    public Type resolveParameterBindType(Class clazz) {
        return this.delegate().resolveParameterBindType(clazz);
    }

}
